/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "bridge/declarative_frontend/engine/jsi/nativeModule/arkts_native_resource_bridge.h"

#include <cstdint>

#include "jsnapi_expo.h"

#include "base/utils/device_config.h"
#include "base/utils/system_properties.h"
#include "bridge/declarative_frontend/engine/jsi/nativeModule/arkts_native_api_bridge.h"
#include "bridge/declarative_frontend/engine/jsi/nativeModule/arkts_utils.h"
#include "core/common/resource/resource_manager.h"
#include "core/components/common/properties/color.h"

namespace OHOS::Ace::NG {
namespace {
constexpr int32_t CALL_ARG_3 = 3;
ColorMode MapJsColorModeToColorMode(int32_t jsColorMode)
{
    switch (jsColorMode) {
        case 1: // 1 is the ThemeColorMode.LIGHT
            return ColorMode::LIGHT;
        case 2: // 2 is the ThemeColorMode.DARK
            return ColorMode::DARK;
        default:
            return ColorMode::COLOR_MODE_UNDEFINED;
    }
    return ColorMode::COLOR_MODE_UNDEFINED;
}

#if defined(ANDROID_PLATFORM) || defined(IOS_PLATFORM)
void UpdateColorModeForThemeConstants(const ColorMode& colorMode)
{
    auto container = Container::Current();
    CHECK_NULL_VOID(container);
    auto resConfig = container->GetResourceConfiguration();
    resConfig.SetColorMode(colorMode);

    auto themeManager = PipelineBase::CurrentThemeManager();
    CHECK_NULL_VOID(themeManager);
    auto themeConstants = themeManager->GetThemeConstants();
    CHECK_NULL_VOID(themeConstants);
    themeConstants->UpdateConfig(resConfig);
}
#endif
} // namespace

ArkUINativeModuleValue ResourceBridge::UpdateColorMode(ArkUIRuntimeCallInfo* runtimeCallInfo)
{
    EcmaVM* vm = runtimeCallInfo->GetVM();
    CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
    Local<JSValueRef> firstArg = runtimeCallInfo->GetCallArgRef(0);
    ColorMode colorModeValue = ColorMode::COLOR_MODE_UNDEFINED;
    if (firstArg->IsNumber()) {
        int32_t firstArgValue = firstArg->Int32Value(vm);
        colorModeValue = MapJsColorModeToColorMode(firstArgValue);
    }
    if (colorModeValue != ColorMode::COLOR_MODE_UNDEFINED) {
        auto pipeline = NG::PipelineContext::GetCurrentContextSafely();
        CHECK_NULL_RETURN(pipeline, panda::JSValueRef::Undefined(vm));
#if defined(ANDROID_PLATFORM) || defined(IOS_PLATFORM)
        UpdateColorModeForThemeConstants(colorModeValue);
#else
        ResourceManager::GetInstance().UpdateColorMode(
            pipeline->GetBundleName(), pipeline->GetModuleName(), pipeline->GetInstanceId(), colorModeValue);
#endif
        pipeline->SetLocalColorMode(colorModeValue);
    }
    return panda::JSValueRef::Undefined(vm);
}

ArkUINativeModuleValue ResourceBridge::Restore(ArkUIRuntimeCallInfo* runtimeCallInfo)
{
    EcmaVM* vm = runtimeCallInfo->GetVM();
    CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));

    auto pipeline = NG::PipelineContext::GetCurrentContextSafely();
    CHECK_NULL_RETURN(pipeline, panda::JSValueRef::Undefined(vm));
    pipeline->SetLocalColorMode(ColorMode::COLOR_MODE_UNDEFINED);

    auto colorModeValue = pipeline->GetColorMode();
#if defined(ANDROID_PLATFORM) || defined(IOS_PLATFORM)
    UpdateColorModeForThemeConstants(colorModeValue);
#else
    ResourceManager::GetInstance().UpdateColorMode(
        pipeline->GetBundleName(), pipeline->GetModuleName(), pipeline->GetInstanceId(), colorModeValue);
#endif
    return panda::JSValueRef::Undefined(vm);
}

ArkUINativeModuleValue ResourceBridge::GetColorValue(ArkUIRuntimeCallInfo* runtimeCallInfo)
{
    EcmaVM* vm = runtimeCallInfo->GetVM();
    CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
    Local<JSValueRef> firstArg = runtimeCallInfo->GetCallArgRef(0);
    Color color;
    if (ArkTSUtils::ParseJsColorAlpha(vm, firstArg, color)) {
        uint32_t colorValue = color.GetValue();
        return panda::NumberRef::New(vm, colorValue);
    }
    return panda::JSValueRef::Undefined(vm);
}

ArkUINativeModuleValue ResourceBridge::GetStringValue(ArkUIRuntimeCallInfo* runtimeCallInfo)
{
    EcmaVM* vm = runtimeCallInfo->GetVM();
    CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
    Local<JSValueRef> firstArg = runtimeCallInfo->GetCallArgRef(0);
    std::string result;
    if (ArkTSUtils::ParseJsString(vm, firstArg, result)) {
        return panda::StringRef::NewFromUtf8(vm, result.c_str());
    }
    return panda::JSValueRef::Undefined(vm);
}

ArkUINativeModuleValue ResourceBridge::GetNumberValue(ArkUIRuntimeCallInfo* runtimeCallInfo)
{
    EcmaVM* vm = runtimeCallInfo->GetVM();
    CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
    Local<JSValueRef> firstArg = runtimeCallInfo->GetCallArgRef(0);
    double result;
    if (ArkTSUtils::ParseJsDouble(vm, firstArg, result)) {
        return panda::NumberRef::New(vm, result);
    }
    return panda::JSValueRef::Undefined(vm);
}

ArkUINativeModuleValue ResourceBridge::ClearCache(ArkUIRuntimeCallInfo* runtimeCallInfo)
{
    EcmaVM* vm = runtimeCallInfo->GetVM();
    CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
    ResourceManager::GetInstance().Reset();
    return panda::JSValueRef::Undefined(vm);
}

ArkUINativeModuleValue ResourceBridge::SetResourceManagerCacheMaxCountForHSP(ArkUIRuntimeCallInfo* runtimeCallInfo)
{
    EcmaVM* vm = runtimeCallInfo->GetVM();
    CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
    auto taskExecutor = Container::CurrentTaskExecutorSafelyWithCheck();
    CHECK_NULL_RETURN(taskExecutor, panda::NativePointerRef::New(vm, nullptr));
    Local<JSValueRef> firstArg = runtimeCallInfo->GetCallArgRef(0);
    size_t cacheSize = 0;
    if (firstArg->IsNumber()) {
        if (taskExecutor->WillRunOnCurrentThread(TaskExecutor::TaskType::UI)) {
            auto realValue = firstArg->ToNumber(vm)->Value();
            auto value = firstArg->Int32Value(vm);
            if (!NearEqual(realValue, value)) {
                ArkTSUtils::ThrowBusinessError(vm,
                    "cache count cannot be a floating-point number, but the input is " + std::to_string(realValue),
                    ERROR_CODE_PARAMETER_TYPE_ERROR);
                return panda::JSValueRef::Undefined(vm);
            }
            if (value < 0) {
                ArkTSUtils::ThrowBusinessError(vm,
                    "cache count cannot be negative, but the input is " + std::to_string(realValue),
                    ERROR_CODE_PARAMETER_LESS_THAN_ZERO);
                return panda::JSValueRef::Undefined(vm);
            }
            cacheSize = static_cast<size_t>(value);
            ResourceManager::GetInstance().SetResourceCacheSize(cacheSize);
        } else {
            ArkTSUtils::ThrowBusinessError(
                vm, "The function cannot be called from a non-main thread", ERROR_CODE_NOT_RUN_ON_UI_THREAD);
            return panda::JSValueRef::Undefined(vm);
        }
    }
    return panda::JSValueRef::Undefined(vm);
}

ArkUINativeModuleValue ResourceBridge::GetResourceId(ArkUIRuntimeCallInfo* runtimeCallInfo)
{
    EcmaVM* vm = runtimeCallInfo->GetVM();
    CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
    int32_t argCount = static_cast<int32_t>(runtimeCallInfo->GetArgsNumber());
    if (argCount != CALL_ARG_3) {
        return panda::JSValueRef::Undefined(vm);
    }
    Local<JSValueRef> firstArg = runtimeCallInfo->GetCallArgRef(0);
    if (!firstArg->IsString(vm)) {
        return panda::NumberRef::New(vm, -1);
    }
    auto resName = firstArg->ToString(vm)->ToString(vm);

    Local<JSValueRef> secondArg = runtimeCallInfo->GetCallArgRef(1);
    if (!secondArg->IsString(vm)) {
        return panda::NumberRef::New(vm, -1);
    }
    auto bundleName = secondArg->ToString(vm)->ToString(vm);

    Local<JSValueRef> thirdArg = runtimeCallInfo->GetCallArgRef(2); // 2 means the arg of moduleName
    if (!thirdArg->IsString(vm)) {
        return panda::NumberRef::New(vm, -1);
    }
    auto moduleName = thirdArg->ToString(vm)->ToString(vm);

    int32_t resId = -1;
    if (!ArkTSUtils::GetResourceId(resName, bundleName, moduleName, resId)) {
        return panda::NumberRef::New(vm, -1);
    }
    return panda::NumberRef::New(vm, resId);
}
} // namespace OHOS::Ace::NG
